// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;

using System.Runtime.InteropServices;

namespace SKNotes.AsyncProtocolHandler
{
	public class PluggableProtocolHandler : 
        NativeMethods.IInternetProtocol,
        NativeMethods.IInternetProtocolRoot
	{
		public PluggableProtocolHandler()
		{
		}

        internal readonly object sync=new object();
        PluggableProtocolRequest m_Request;
        PluggableProtocolResponse m_Response;
        bool m_Started;
               
        public PluggableProtocolRequest Request
        {
            get { lock( sync ) return m_Request; }
        }

        public PluggableProtocolResponse Response
        {
            get { lock( sync ) return m_Response; }
        }

        public bool Started
        {
            get { lock( sync ) return m_Started; }
        }

        public event EventHandler ProtocolStart;
        public event AbortEventHandler ProtocolAbort;

        protected virtual void OnProtocolStart(EventArgs e)
        {
            EventHandler temp = ProtocolStart;
            if (temp != null)
            {
                temp(this,e);
            }
        }

        protected virtual void OnProtocolAbort(AbortEventArgs e)
        {
            AbortEventHandler temp = ProtocolAbort;
            if (temp != null)
            {
                temp(this, e);
            }
        }

        void ErrorHandledOnStart(EventArgs e)
        {
            try
            {
                OnProtocolStart(e);
            }
            catch( Exception err )
            {
                if( Response.OutputStream!=null
                    && Response.OutputStream.CanWrite )
                {
                    if( Response.ContentType+""=="" )
                        Response.ContentType="text/html";
                    Response.WriteLine("");
                    Response.WriteLine("<hr>========");
                    Response.WriteLine("<pre>");
                    Response.WriteLine(err);
                    Response.WriteLine("</pre>");
                    Response.EndResponse();
                }
                else
                {
                    //throw err;
                }
            }
        }

        void Start(
            string szUrl,
            NativeMethods.IInternetProtocolSink pOIProtSink,
            NativeMethods.IInternetBindInfo pOIBindInfo,
            NativeMethods.PI_FLAGS grfPI,
            int dwReserved )
        {
            lock( sync )
            {
                m_Request=new PluggableProtocolRequest( this, szUrl, pOIBindInfo, grfPI );

                m_Response=new PluggableProtocolResponse( this, pOIProtSink );
         
                m_Started=true;
            }
            ErrorHandledOnStart(EventArgs.Empty);
        }

        

        void Terminate(int dwOptions)
        {
            Dispose();
        }
        
        void Seek(long dlibMove, int dwOrigin, out long plibNewPosition)
        {
            throw new NotImplementedException();
        }

        void LockRequest(int dwOptions)
        {
        }

        void UnlockRequest()
        {
        }

        void Abort(int hrReason, int dwOptions)
        {
            Exception abortReason=null;
            try 
            {
                Marshal.ThrowExceptionForHR(hrReason); 
            }
            catch( Exception getError )
            {
                abortReason=getError;
            }

            OnProtocolAbort( new AbortEventArgs(abortReason) );

            Response.NotifySinkAbort(abortReason);

            Dispose();
        }

        void Suspend()
        {
            // Documentation says, Not currently implemented
        }

        void Resume()
        {
            // Documentation says, Not currently implemented
        }

        int Read( IntPtr pv, int cb, out int pcbRead )
        {
            return Response.ProtocolRead( pv, cb, out pcbRead );
        }

        void Continue(ref NativeMethods.PROTOCOLDATA pProtocolData)
        {
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected void Dispose(bool isDisposing)
        {
            GC.SuppressFinalize(this);
            if( isDisposing )
            {
                lock( sync )
                {
                    if( m_Response!=null )
                    {
                        m_Response.Dispose();
                        m_Response=null;
                    }

                    if( m_Request!=null )
                    {
                        m_Request.Dispose();
                        m_Request=null;
                    }
                }
            }
        }

        ~PluggableProtocolHandler()
        {
            Dispose(false);
        }

        #region IInternetProtocol Members

        void NativeMethods.IInternetProtocol.Start(
            string szUrl,
            NativeMethods.IInternetProtocolSink pOIProtSink,
            NativeMethods.IInternetBindInfo pOIBindInfo,
            NativeMethods.PI_FLAGS grfPI,
            int dwReserved )
        {
            Start(szUrl,pOIProtSink,pOIBindInfo,grfPI,dwReserved);
        }

        void NativeMethods.IInternetProtocol.Resume()
        {
            Resume();
        }

        void NativeMethods.IInternetProtocol.Terminate(int dwOptions)
        {
            Terminate(dwOptions);
        }

        void NativeMethods.IInternetProtocol.Seek(long dlibMove, int dwOrigin, out long plibNewPosition)
        {
            Seek(dlibMove,dwOrigin,out plibNewPosition);
        }

        void NativeMethods.IInternetProtocol.LockRequest(int dwOptions)
        {
            LockRequest(dwOptions);
        }

        void NativeMethods.IInternetProtocol.UnlockRequest()
        {
            UnlockRequest();
        }

        void NativeMethods.IInternetProtocol.Abort(int hrReason, int dwOptions)
        {
            Abort(hrReason,dwOptions);
        }

        void NativeMethods.IInternetProtocol.Suspend()
        {
            Suspend();
        }

        int NativeMethods.IInternetProtocol.Read( IntPtr pv, int cb, out int pcbRead )
        {
            int result=Read(pv,cb,out pcbRead);
            return result;
        }

        void NativeMethods.IInternetProtocol.Continue(ref NativeMethods.PROTOCOLDATA pProtocolData)
        {
            Continue(ref pProtocolData);
        }

        #endregion
    
        #region IInternetProtocolRoot Members

        void NativeMethods.IInternetProtocolRoot.Start(
            string szUrl,
            NativeMethods.IInternetProtocolSink pOIProtSink,
            NativeMethods.IInternetBindInfo pOIBindInfo,
            NativeMethods.PI_FLAGS grfPI,
            int dwReserved )
        {
            Start(szUrl,pOIProtSink,pOIBindInfo,grfPI,dwReserved);
        }

        void NativeMethods.IInternetProtocolRoot.Resume()
        {
            Resume();
        }

        void NativeMethods.IInternetProtocolRoot.Terminate(int dwOptions)
        {
            Terminate(dwOptions);
        }

        void NativeMethods.IInternetProtocolRoot.Abort(int hrReason, int dwOptions)
        {
            Abort(hrReason,dwOptions);
        }

        void NativeMethods.IInternetProtocolRoot.Suspend()
        {
            Suspend();
        }

        void NativeMethods.IInternetProtocolRoot.Continue(ref NativeMethods.PROTOCOLDATA pProtocolData)
        {
            Continue(ref pProtocolData);
        }

        #endregion

	}
}
